﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace Brouteforce.utilities {
    public static class Geometrics {

        /// <summary>
        /// return a line that is defined from center to to center
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static Vector4 CentersLine(Rectangle a, Rectangle b) {
            var c1 = a.Center;
            var c2 = b.Center;
            return new Vector4(c1.X, c1.Y, c2.X, c2.Y);
        }

        /// <summary>
        /// return true if the given point located on the given line SEGMENT
        /// </summary>
        /// <param name="point"></param>
        /// <param name="lineSegment"></param>
        /// <returns></returns>
        public static bool IsOnLineSegment(Vector2 point, Vector4 lineSegment) {
            if (!IsOnLine(point, lineSegment))
                return false;

            if (lineSegment.Z < lineSegment.X) {
                var t = lineSegment.X;
                lineSegment.X = lineSegment.Z;
                lineSegment.Z = t;
            }

            if (lineSegment.W < lineSegment.Y) {
                var t = lineSegment.W;
                lineSegment.W = lineSegment.Y;
                lineSegment.Y = t;
            }

            return point.X >= lineSegment.X && point.X <= lineSegment.Z && point.Y >= lineSegment.Y &&
                   point.Y <= lineSegment.W;

        }

        public static double LineSlope(Vector4 line) {
            return (line.Y - line.W) / (line.X - line.Z);
        }

        public static double FindLineY(Vector4 line, double x) {
            // y - y1 = m(x - x1) ==> y = mx -mx1 + y1
            var m = LineSlope(line);
            if (double.IsInfinity(m))
                return 0;

            return m * x - m * line.X + line.Y;
        }

        public static double FindLineX(Vector4 line, double y) {
            // y - y1 = m(x - x1) ==> x = (y - y1 + mx1) / m

            var m = LineSlope(line);
            if (double.IsInfinity(m))
                return line.X;

            return (y - line.Y + m * line.X) / m;
        }

        public static bool IsOnLine(Vector2 point, Vector4 line) {
            // y - y1 = m(x - x1) ==> y = mx -mx1 + y1
            var m = LineSlope(line);
            if (double.IsInfinity(m))
                return Math.Abs(line.X - point.X) < 0.0000001;

            return Math.Abs(point.Y - FindLineY(line, point.X)) < 0.00001;
        }

        public static Vector2? LinesIntersectPoint(Vector4 line1, Vector4 line2) {
            // y - y1 = m(x - x1) => y = m(x - x1) + y1 => y = mx + y1 - mx1

            // y1 = y2 => m1x + y11 -m1x11 = m2x + y21 -m2x21 
            //         => m1x -m2x = y21 -m2x21 -y11 +m1x11 
            //         => x = (y21 -m2x21 -y11 +m1x11) / (m1 - m2)

            var m1 = LineSlope(line1);
            var m2 = LineSlope(line2);

            if (double.IsInfinity(m1) && double.IsInfinity(m2)) {
                if (Math.Abs(line1.X - line2.X) < 0.000001)
                    return new Vector2(line1.X, 0);
                else
                    return null;
            } else if (double.IsInfinity(m1)) {
                return new Vector2(line1.X, (float)FindLineY(line2, line1.X));
            } else if (double.IsInfinity(m2)) {
                return new Vector2(line2.X, (float)FindLineY(line1, line2.X));
            }

            if (Math.Abs(m2 - m1) < 0.00001) return null;

            var x = (line2.Y - m2 * line2.X - line1.X + m1 * line1.X) / (m1 - m2);
            var y = FindLineY(line1, x);

            return new Vector2((float)x, (float)y);
        }

        /// <summary>
        /// check if the given point is inside or on the borders of the given rectengle
        /// </summary>
        /// <param name="point"></param>
        /// <param name="rect"></param>
        /// <returns></returns>
        public static bool IsOnRect(Vector2 point, Rectangle rect) {
            return point.X >= rect.X && point.X <= rect.X + rect.Width && point.Y >= rect.Y &&
                   point.Y <= rect.Y + rect.Height;
        }

        public static bool IsRectIntersectLineSegment(Vector4 line, Rectangle rect) {
            Vector2? intersection = null;

            //check ---
            var checkedLine = new Vector4(rect.X, rect.Y, rect.X + rect.Width, rect.Y);
            intersection = LinesIntersectPoint(line, checkedLine);
            if (intersection.HasValue && IsOnLineSegment(intersection.Value, line) && IsOnLineSegment(intersection.Value, checkedLine))
                return true;

            //check ___
            checkedLine = new Vector4(rect.X, rect.Y + rect.Height, rect.X + rect.Width, rect.Y + rect.Height);
            intersection = LinesIntersectPoint(line, checkedLine);
            if (intersection.HasValue && IsOnLineSegment(intersection.Value, line) && IsOnLineSegment(intersection.Value, checkedLine))
                return true;

            //check |..
            checkedLine = new Vector4(rect.X, rect.Y, rect.X, rect.Y + rect.Height);
            intersection = LinesIntersectPoint(line, checkedLine);
            if (intersection.HasValue && IsOnLineSegment(intersection.Value, line) && IsOnLineSegment(intersection.Value, checkedLine))
                return true;

            //check ..|
            checkedLine = new Vector4(rect.X + rect.Width, rect.Y, rect.X + rect.Width, rect.Y + rect.Height);
            intersection = LinesIntersectPoint(line, checkedLine);
            if (intersection.HasValue && IsOnLineSegment(intersection.Value, line) && IsOnLineSegment(intersection.Value, checkedLine))
                return true;

            return false;
        }

        public static double Distance(Vector2 point1, Vector2 point2) {
            return Math.Sqrt(Math.Pow(point2.X - point1.X, 2) + Math.Pow(point2.Y - point1.Y, 2));
        }

        public static Vector4 ShiftLine(Vector4 line, double shiftX, double shiftY){
            return new Vector4((float) (line.X + shiftX), (float) (line.Y + shiftY), (float) (line.Z + shiftX), (float) (line.W + shiftY));
        }

        public static Vector4 GetLineSegment(Vector4 line, int offset, int length) {
            Vector2 startPoint = new Vector2(line.X, line.Y);
            Vector2 endPoint = new Vector2(line.Z, line.W);

            Vector2 edge = endPoint - startPoint;
            edge.Normalize();
            startPoint = startPoint + edge * offset;

            edge = endPoint - startPoint;
            edge.Normalize();
            endPoint = startPoint + edge * length;


            var ret = new Vector4(startPoint.X, startPoint.Y, endPoint.X, endPoint.Y);

            return ret;
        }
    }
}
